unit timer;

interface

uses
	jfunc;

const
	clock8253=1193181.8181;
	intClock8253=round(clock8253);
	countExact=4971;
	countInterval=trunc(65536/countExact);
	freq=clock8253/countExact;

	PIT_Data0=$40;
	PIT_Data1=$41;
	PIT_Data2=$42;
	PIT_Control=$43;

	PIT_Select0=$00;
	PIT_Select1=$40;
	PIT_Select2=$80;

	PIT_Latch=$00;
	PIT_LSB=$10;
	PIT_MSB=$20;
	PIT_LSBMSB=$30;

	PIT_Mode0=$00; { Interrupt on Terminal Count }
	PIT_Mode1=$02; { Hardware Retriggerable One-Shot }
	PIT_Mode2=$04; { Rate Generator }
	PIT_Mode3=$06; { Square Wave }
	PIT_Mode4=$08; { Software Triggered Strobe }
	PIT_Mode5=$0A; { Hardware Triggered Strobe }

	PIT_BCD=$01;

var
	userClockCounter:word;

procedure startTimer;
procedure killTimer;
function getUserClockInterval(hz:word):word;
function userTimerExpired(interval:word):boolean;

implementation

var
	isrCount:word;
	timerActive:boolean;
	oldISR,
	oldExitProc:pointer;

function getUserClockInterval(hz:word):word;
begin
	getUserClockInterval:=trunc(freq/hz);
end;

procedure timerISR; interrupt; assembler;
asm
	inc  userClockCounter
	dec  isrCount
	jnz   @done
	mov  ax,countInterval
	mov  isrCount,ax
	pushf
	call oldISR
@done:
end;

procedure startTimer;
var
	t:integer;
	d:word;
begin
	isrCount:=countInterval;
	setIntVec($1C,@timerISR);
	asm
		{ set timer 0 to desired high speed frequency }
		mov  al,PIT_Select0 or PIT_LSBMSB or PIT_Mode2
		out  PIT_Control,al
		mov  ax,countExact
		out  PIT_Data0,al
		mov  al,ah
		out  PIT_Data0,al
	end;
	timerActive:=true;
end;

procedure killTimer;
begin
	asm
		{ reset timer0 to normal }
		mov  al,PIT_Select0 or PIT_LSBMSB or PIT_Mode2
		out  PIT_Control,al
		mov  ax,$FFFF
		out  PIT_Data0,al
		mov  al,ah
		out  PIT_Data0,al
	end;
	setIntVec($1C,oldISR);
	timerActive:=false;
end;

function userTimerExpired(interval:word):boolean;
begin
	if userClockCounter>=interval then begin
		userClockCounter:=userClockCounter-interval;
		userTimerExpired:=true;
	end else userTimerExpired:=false;
end;

procedure safeExit; far;
begin
	if timerActive then killTimer;
	exitProc:=oldExitProc;
end;

begin
	timerActive:=false;
	oldExitProc:=exitproc;
	exitproc:=@safeExit;
	oldISR:=getIntVec($1C);
end.